Passed
Pull Request — master (#10)
by Mark
02:48
created

ol-layerswitcher.js ➔ isTouchDevice_   A

Complexity

Conditions 2

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 2
1
(function (global, factory) {
2
	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('ol/control/Control'), require('ol/Observable'), require('ol/layer/Group')) :
3
	typeof define === 'function' && define.amd ? define(['ol/control/Control', 'ol/Observable', 'ol/layer/Group'], factory) :
0 ignored issues
show
Bug introduced by
The variable define seems to be never declared. If this is a global, consider adding a /** global: define */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
4
	(global.LayerSwitcher = factory(global.ol.control.Control,global.ol.Observable,global.ol.layer.Group));
5
}(this, (function (Control,ol_Observable,LayerGroup) { 'use strict';
6
7
Control = 'default' in Control ? Control['default'] : Control;
8
LayerGroup = 'default' in LayerGroup ? LayerGroup['default'] : LayerGroup;
9
10
var classCallCheck = function (instance, Constructor) {
11
  if (!(instance instanceof Constructor)) {
12
    throw new TypeError("Cannot call a class as a function");
13
  }
14
};
15
16
var createClass = function () {
17
  function defineProperties(target, props) {
18
    for (var i = 0; i < props.length; i++) {
19
      var descriptor = props[i];
20
      descriptor.enumerable = descriptor.enumerable || false;
21
      descriptor.configurable = true;
22
      if ("value" in descriptor) descriptor.writable = true;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
23
      Object.defineProperty(target, descriptor.key, descriptor);
24
    }
25
  }
26
27
  return function (Constructor, protoProps, staticProps) {
28
    if (protoProps) defineProperties(Constructor.prototype, protoProps);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
29
    if (staticProps) defineProperties(Constructor, staticProps);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
30
    return Constructor;
31
  };
32
}();
33
34
35
36
37
38
39
40
var get = function get(object, property, receiver) {
41
  if (object === null) object = Function.prototype;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
42
  var desc = Object.getOwnPropertyDescriptor(object, property);
43
44
  if (desc === undefined) {
45
    var parent = Object.getPrototypeOf(object);
46
47
    if (parent === null) {
48
      return undefined;
49
    } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
50
      return get(parent, property, receiver);
51
    }
52
  } else if ("value" in desc) {
53
    return desc.value;
54
  } else {
55
    var getter = desc.get;
56
57
    if (getter === undefined) {
58
      return undefined;
59
    }
60
61
    return getter.call(receiver);
62
  }
63
};
64
65
var inherits = function (subClass, superClass) {
66
  if (typeof superClass !== "function" && superClass !== null) {
67
    throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
68
  }
69
70
  subClass.prototype = Object.create(superClass && superClass.prototype, {
71
    constructor: {
72
      value: subClass,
73
      enumerable: false,
74
      writable: true,
75
      configurable: true
76
    }
77
  });
78
  if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
79
};
80
81
82
83
84
85
86
87
88
89
90
91
var possibleConstructorReturn = function (self, call) {
92
  if (!self) {
93
    throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
94
  }
95
96
  return call && (typeof call === "object" || typeof call === "function") ? call : self;
97
};
98
99
/**
100
 * @protected
101
 */
102
var CSS_PREFIX = 'layer-switcher-';
103
/**
104
 * OpenLayers LayerSwitcher Control, displays a list of layers and groups
105
 * associated with a map which have a `title` property.
106
 *
107
 * To be shown in the LayerSwitcher panel layers must have a `title` property;
108
 * base map layers should have a `type` property set to `base`. Group layers
109
 * (`LayerGroup`) can be used to visually group layers together; a group
110
 * with a `fold` property set to either `'open'` or `'close'` will be displayed
111
 * with a toggle.
112
 *
113
 * See [BaseLayerOptions](#baselayeroptions) for a full list of LayerSwitcher
114
 * properties for layers (`TileLayer`, `ImageLayer`, `VectorTile` etc.) and
115
 * [GroupLayerOptions](#grouplayeroptions) for group layer (`LayerGroup`)
116
 * LayerSwitcher properties.
117
 *
118
 * Layer and group properties can either be set by adding extra properties
119
 * to their options when they are created or via their set method.
120
 *
121
 * Specify a `title` for a Layer by adding a `title` property to it's options object:
122
 * ```javascript
123
 * var lyr = new ol.layer.Tile({
124
 *   // Specify a title property which will be displayed by the layer switcher
125
 *   title: 'OpenStreetMap',
126
 *   visible: true,
127
 *   source: new ol.source.OSM()
128
 * })
129
 * ```
130
 *
131
 * Alternatively the properties can be set via the `set` method after a layer has been created:
132
 * ```javascript
133
 * var lyr = new ol.layer.Tile({
134
 *   visible: true,
135
 *   source: new ol.source.OSM()
136
 * })
137
 * // Specify a title property which will be displayed by the layer switcher
138
 * lyr.set('title', 'OpenStreetMap');
139
 * ```
140
 *
141
 * To create a LayerSwitcher and add it to a map, create a new instance then pass it to the map's [`addControl` method](https://openlayers.org/en/latest/apidoc/module-ol_PluggableMap-PluggableMap.html#addControl).
142
 * ```javascript
143
 * var layerSwitcher = new LayerSwitcher({
144
 *   reverse: true,
145
 *   groupSelectStyle: 'group'
146
 * });
147
 * map.addControl(layerSwitcher);
148
 * ```
149
 *
150
 * @constructor
151
 * @extends {ol/control/Control~Control}
152
 * @param opt_options LayerSwitcher options, see  [LayerSwitcher Options](#options) and [RenderOptions](#renderoptions) which LayerSwitcher `Options` extends for more details.
153
 */
154
155
var LayerSwitcher = function (_Control) {
156
    inherits(LayerSwitcher, _Control);
157
158
    function LayerSwitcher(opt_options) {
159
        classCallCheck(this, LayerSwitcher);
160
161
        var options = Object.assign({}, opt_options);
162
        // TODO Next: Rename to showButtonTitle
163
        var tipLabel = options.tipLabel ? options.tipLabel : 'Legend';
164
        // TODO Next: Rename to hideButtonTitle
165
        var collapseTipLabel = options.collapseTipLabel ? options.collapseTipLabel : 'Collapse legend';
166
        var element = document.createElement('div');
167
168
        var _this = possibleConstructorReturn(this, (LayerSwitcher.__proto__ || Object.getPrototypeOf(LayerSwitcher)).call(this, { element: element, target: options.target }));
169
170
        _this.activationMode = options.activationMode || 'mouseover';
171
        _this.startActive = options.startActive === true;
172
        // TODO Next: Rename to showButtonContent
173
        var label = options.label !== undefined ? options.label : '';
174
        // TODO Next: Rename to hideButtonContent
175
        var collapseLabel = options.collapseLabel !== undefined ? options.collapseLabel : '\xBB';
176
        _this.groupSelectStyle = LayerSwitcher.getGroupSelectStyle(options.groupSelectStyle);
177
        _this.reverse = options.reverse !== false;
178
        _this.mapListeners = [];
179
        _this.hiddenClassName = 'ol-unselectable ol-control layer-switcher';
180
        if (LayerSwitcher.isTouchDevice_()) {
181
            _this.hiddenClassName += ' touch';
182
        }
183
        _this.shownClassName = 'shown';
184
        element.className = _this.hiddenClassName;
185
        var button = document.createElement('button');
186
        button.setAttribute('title', tipLabel);
187
        button.setAttribute('aria-label', tipLabel);
188
        element.appendChild(button);
189
        _this.panel = document.createElement('div');
190
        _this.panel.className = 'panel';
191
        element.appendChild(_this.panel);
192
        LayerSwitcher.enableTouchScroll_(_this.panel);
193
        button.textContent = label;
194
        element.classList.add(CSS_PREFIX + 'group-select-style-' + _this.groupSelectStyle);
195
        element.classList.add(CSS_PREFIX + 'activation-mode-' + _this.activationMode);
196
        if (_this.activationMode === 'click') {
197
            // TODO Next: Remove in favour of layer-switcher-activation-mode-click
198
            element.classList.add('activationModeClick');
199
            if (_this.startActive) {
200
                button.textContent = collapseLabel;
201
                button.setAttribute('title', collapseTipLabel);
202
                button.setAttribute('aria-label', collapseTipLabel);
203
            }
204
            button.onclick = function (e) {
205
                var evt = e || window.event;
206
                if (_this.element.classList.contains(_this.shownClassName)) {
207
                    _this.hidePanel();
208
                    button.textContent = label;
209
                    button.setAttribute('title', tipLabel);
210
                    button.setAttribute('aria-label', tipLabel);
211
                } else {
212
                    _this.showPanel();
213
                    button.textContent = collapseLabel;
214
                    button.setAttribute('title', collapseTipLabel);
215
                    button.setAttribute('aria-label', collapseTipLabel);
216
                }
217
                evt.preventDefault();
218
            };
219
        } else {
220
            button.onmouseover = function () {
221
                _this.showPanel();
222
            };
223
            button.onclick = function (e) {
224
                var evt = e || window.event;
225
                _this.showPanel();
226
                evt.preventDefault();
227
            };
228
            _this.panel.onmouseout = function (evt) {
229
                if (!_this.panel.contains(evt.relatedTarget)) {
230
                    _this.hidePanel();
231
                }
232
            };
233
        }
234
        return _this;
235
    }
236
    /**
237
     * Set the map instance the control is associated with.
238
     * @param map The map instance.
239
     */
240
241
242
    createClass(LayerSwitcher, [{
243
        key: 'setMap',
244
        value: function setMap(map) {
245
            var _this2 = this;
246
247
            // Clean up listeners associated with the previous map
248
            for (var i = 0; i < this.mapListeners.length; i++) {
249
                ol_Observable.unByKey(this.mapListeners[i]);
250
            }
251
            this.mapListeners.length = 0;
252
            // Wire up listeners etc. and store reference to new map
253
            get(LayerSwitcher.prototype.__proto__ || Object.getPrototypeOf(LayerSwitcher.prototype), 'setMap', this).call(this, map);
254
            if (map) {
255
                if (this.startActive) {
256
                    this.showPanel();
257
                } else {
258
                    this.renderPanel();
259
                }
260
                if (this.activationMode !== 'click') {
261
                    this.mapListeners.push(map.on('pointerdown', function () {
262
                        _this2.hidePanel();
263
                    }));
264
                }
265
            }
266
        }
267
        /**
268
         * Show the layer panel.
269
         */
270
271
    }, {
272
        key: 'showPanel',
273
        value: function showPanel() {
274
            if (!this.element.classList.contains(this.shownClassName)) {
275
                this.element.classList.add(this.shownClassName);
276
                this.renderPanel();
277
            }
278
        }
279
        /**
280
         * Hide the layer panel.
281
         */
282
283
    }, {
284
        key: 'hidePanel',
285
        value: function hidePanel() {
286
            if (this.element.classList.contains(this.shownClassName)) {
287
                this.element.classList.remove(this.shownClassName);
288
            }
289
        }
290
        /**
291
         * Re-draw the layer panel to represent the current state of the layers.
292
         */
293
294
    }, {
295
        key: 'renderPanel',
296
        value: function renderPanel() {
297
            this.dispatchEvent('render');
298
            LayerSwitcher.renderPanel(this.getMap(), this.panel, {
299
                groupSelectStyle: this.groupSelectStyle,
300
                reverse: this.reverse
301
            });
302
            this.dispatchEvent('rendercomplete');
303
        }
304
        /**
305
         * **_[static]_** - Re-draw the layer panel to represent the current state of the layers.
306
         * @param map The OpenLayers Map instance to render layers for
307
         * @param panel The DOM Element into which the layer tree will be rendered
308
         * @param options Options for panel, group, and layers
309
         */
310
311
    }], [{
312
        key: 'renderPanel',
313
        value: function renderPanel(map, panel, options) {
314
            // Create the event.
315
            var render_event = new Event('render');
0 ignored issues
show
Bug introduced by
The variable Event seems to be never declared. If this is a global, consider adding a /** global: Event */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
316
            // Dispatch the event.
317
            panel.dispatchEvent(render_event);
318
            options = options || {};
319
            options.groupSelectStyle = LayerSwitcher.getGroupSelectStyle(options.groupSelectStyle);
320
            LayerSwitcher.ensureTopVisibleBaseLayerShown(map, options.groupSelectStyle);
321
            while (panel.firstChild) {
322
                panel.removeChild(panel.firstChild);
323
            }
324
            // Reset indeterminate state for all layers and groups before
325
            // applying based on groupSelectStyle
326
            LayerSwitcher.forEachRecursive(map, function (l, _idx, _a) {
0 ignored issues
show
Unused Code introduced by
The parameter _idx is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter _a is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
327
                l.set('indeterminate', false);
328
            });
329
            if (options.groupSelectStyle === 'children' || options.groupSelectStyle === 'none') {
330
                // Set visibile and indeterminate state of groups based on
331
                // their children's visibility
332
                LayerSwitcher.setGroupVisibility(map);
333
            } else if (options.groupSelectStyle === 'group') {
334
                // Set child indetermiate state based on their parent's visibility
335
                LayerSwitcher.setChildVisibility(map);
336
            }
337
            var ul = document.createElement('ul');
338
            panel.appendChild(ul);
339
            // passing two map arguments instead of lyr as we're passing the map as the root of the layers tree
340
            LayerSwitcher.renderLayers_(map, map, ul, options, function render(_changedLyr) {
0 ignored issues
show
Unused Code introduced by
The parameter _changedLyr is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
341
                LayerSwitcher.renderPanel(map, panel, options);
342
            });
343
            // Create the event.
344
            var rendercomplete_event = new Event('rendercomplete');
345
            // Dispatch the event.
346
            panel.dispatchEvent(rendercomplete_event);
347
        }
348
        /**
349
         * **_[static]_** - Determine if a given layer group contains base layers
350
         * @param grp Group to test
351
         */
352
353
    }, {
354
        key: 'isBaseGroup',
355
        value: function isBaseGroup(grp) {
356
            if (grp instanceof LayerGroup) {
357
                var lyrs = grp.getLayers().getArray();
358
                return lyrs.length && lyrs[0].get('type') === 'base';
359
            } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
360
                return false;
361
            }
362
        }
363
    }, {
364
        key: 'setGroupVisibility',
365
        value: function setGroupVisibility(map) {
366
            // Get a list of groups, with the deepest first
367
            var groups = LayerSwitcher.getGroupsAndLayers(map, function (l) {
368
                return l instanceof LayerGroup && !l.get('combine') && !LayerSwitcher.isBaseGroup(l);
369
            }).reverse();
370
            // console.log(groups.map(g => g.get('title')));
371
            groups.forEach(function (grp) {
372
                // TODO Can we use getLayersArray, is it public in the esm build?
373
                var descendantVisibility = grp.getLayersArray().map(function (l) {
374
                    var state = l.getVisible();
375
                    // console.log('>', l.get('title'), state);
376
                    return state;
377
                });
378
                // console.log(descendantVisibility);
379
                if (descendantVisibility.every(function (v) {
380
                    return v === true;
381
                })) {
382
                    grp.setVisible(true);
383
                    grp.set('indeterminate', false);
384
                } else if (descendantVisibility.every(function (v) {
385
                    return v === false;
386
                })) {
387
                    grp.setVisible(false);
388
                    grp.set('indeterminate', false);
389
                } else {
390
                    grp.setVisible(true);
391
                    grp.set('indeterminate', true);
392
                }
393
            });
394
        }
395
    }, {
396
        key: 'setChildVisibility',
397
        value: function setChildVisibility(map) {
398
            // console.log('setChildVisibility');
399
            var groups = LayerSwitcher.getGroupsAndLayers(map, function (l) {
400
                return l instanceof LayerGroup && !l.get('combine') && !LayerSwitcher.isBaseGroup(l);
401
            });
402
            groups.forEach(function (grp) {
403
                var group = grp;
404
                // console.log(group.get('title'));
405
                var groupVisible = group.getVisible();
406
                var groupIndeterminate = group.get('indeterminate');
407
                group.getLayers().getArray().forEach(function (l) {
408
                    l.set('indeterminate', false);
409
                    if ((!groupVisible || groupIndeterminate) && l.getVisible()) {
410
                        l.set('indeterminate', true);
411
                    }
412
                });
413
            });
414
        }
415
        /**
416
         * Ensure only the top-most base layer is visible if more than one is visible.
417
         * @param map The map instance.
418
         * @param groupSelectStyle
419
         * @protected
420
         */
421
422
    }, {
423
        key: 'ensureTopVisibleBaseLayerShown',
424
        value: function ensureTopVisibleBaseLayerShown(map, groupSelectStyle) {
425
            var lastVisibleBaseLyr = void 0;
0 ignored issues
show
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
426
            LayerSwitcher.forEachRecursive(map, function (lyr, _idx, _arr) {
0 ignored issues
show
Unused Code introduced by
The parameter _arr is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter _idx is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
427
                if (lyr.get('type') === 'base' && lyr.getVisible()) {
428
                    lastVisibleBaseLyr = lyr;
429
                }
430
            });
431
            if (lastVisibleBaseLyr) LayerSwitcher.setVisible_(map, lastVisibleBaseLyr, true, groupSelectStyle);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
432
        }
433
        /**
434
         * **_[static]_** - Get an Array of all layers and groups displayed by the LayerSwitcher (has a `'title'` property)
435
         * contained by the specified map or layer group; optionally filtering via `filterFn`
436
         * @param grp The map or layer group for which layers are found.
437
         * @param filterFn Optional function used to filter the returned layers
438
         */
439
440
    }, {
441
        key: 'getGroupsAndLayers',
442
        value: function getGroupsAndLayers(grp, filterFn) {
443
            var layers = [];
444
            filterFn = filterFn || function (_lyr, _idx, _arr) {
0 ignored issues
show
Unused Code introduced by
The parameter _lyr is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter _idx is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter _arr is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
445
                return true;
446
            };
447
            LayerSwitcher.forEachRecursive(grp, function (lyr, idx, arr) {
448
                if (lyr.get('title')) {
449
                    if (filterFn(lyr, idx, arr)) {
450
                        layers.push(lyr);
451
                    }
452
                }
453
            });
454
            return layers;
455
        }
456
        /**
457
         * Toggle the visible state of a layer.
458
         * Takes care of hiding other layers in the same exclusive group if the layer
459
         * is toggle to visible.
460
         * @protected
461
         * @param map The map instance.
462
         * @param lyr layer whose visibility will be toggled.
463
         * @param visible Set whether the layer is shown
464
         * @param groupSelectStyle
465
         * @protected
466
         */
467
468
    }, {
469
        key: 'setVisible_',
470
        value: function setVisible_(map, lyr, visible, groupSelectStyle) {
471
            // console.log(lyr.get('title'), visible, groupSelectStyle);
472
            lyr.setVisible(visible);
473
            if (visible && lyr.get('type') === 'base') {
474
                // Hide all other base layers regardless of grouping
475
                LayerSwitcher.forEachRecursive(map, function (l, _idx, _arr) {
0 ignored issues
show
Unused Code introduced by
The parameter _idx is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter _arr is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
476
                    if (l != lyr && l.get('type') === 'base') {
477
                        l.setVisible(false);
478
                    }
479
                });
480
            }
481
            if (lyr instanceof LayerGroup && !lyr.get('combine') && groupSelectStyle === 'children') {
482
                lyr.getLayers().forEach(function (l) {
483
                    LayerSwitcher.setVisible_(map, l, lyr.getVisible(), groupSelectStyle);
484
                });
485
            }
486
        }
487
        /**
488
         * Render all layers that are children of a group.
489
         * @param map The map instance.
490
         * @param lyr Layer to be rendered (should have a title property).
491
         * @param idx Position in parent group list.
492
         * @param options Options for groups and layers
493
         * @protected
494
         */
495
496
    }, {
497
        key: 'renderLayer_',
498
        value: function renderLayer_(map, lyr, idx, options, render) {
499
            var li = document.createElement('li');
500
            var lyrTitle = lyr.get('title');
501
            var checkboxId = LayerSwitcher.uuid();
502
            var label = document.createElement('label');
503
            if (lyr instanceof LayerGroup && !lyr.get('combine')) {
504
                var isBaseGroup = LayerSwitcher.isBaseGroup(lyr);
505
                li.classList.add('group');
506
                if (isBaseGroup) {
507
                    li.classList.add(CSS_PREFIX + 'base-group');
508
                }
509
                // Group folding
510
                if (lyr.get('fold')) {
511
                    li.classList.add(CSS_PREFIX + 'fold');
512
                    li.classList.add(CSS_PREFIX + lyr.get('fold'));
513
                    var btn = document.createElement('button');
514
                    btn.onclick = function (e) {
515
                        var evt = e || window.event;
516
                        LayerSwitcher.toggleFold_(lyr, li);
517
                        evt.preventDefault();
518
                    };
519
                    li.appendChild(btn);
520
                }
521
                if (!isBaseGroup && options.groupSelectStyle != 'none') {
522
                    var input = document.createElement('input');
523
                    input.type = 'checkbox';
524
                    input.id = checkboxId;
525
                    input.checked = lyr.getVisible();
526
                    input.indeterminate = lyr.get('indeterminate');
527
                    input.onchange = function (e) {
528
                        var target = e.target;
529
                        LayerSwitcher.setVisible_(map, lyr, target.checked, options.groupSelectStyle);
530
                        render(lyr);
531
                    };
532
                    li.appendChild(input);
533
                    label.htmlFor = checkboxId;
534
                }
535
                label.innerHTML = lyrTitle;
536
                li.appendChild(label);
537
                var ul = document.createElement('ul');
538
                li.appendChild(ul);
539
                LayerSwitcher.renderLayers_(map, lyr, ul, options, render);
540
            } else {
541
                li.className = 'layer';
542
                var _input = document.createElement('input');
543
                if (lyr.get('type') === 'base') {
544
                    _input.type = 'radio';
545
                    _input.name = 'base';
546
                } else {
547
                    _input.type = 'checkbox';
548
                }
549
                _input.id = checkboxId;
550
                _input.checked = lyr.get('visible');
551
                _input.indeterminate = lyr.get('indeterminate');
552
                _input.onchange = function (e) {
553
                    var target = e.target;
554
                    LayerSwitcher.setVisible_(map, lyr, target.checked, options.groupSelectStyle);
555
                    render(lyr);
556
                };
557
                li.appendChild(_input);
558
                label.htmlFor = checkboxId;
559
                label.innerHTML = lyrTitle;
560
                var rsl = map.getView().getResolution();
561
                if (rsl > lyr.getMaxResolution() || rsl < lyr.getMinResolution()) {
562
                    label.className += ' disabled';
563
                }
564
                li.appendChild(label);
565
            }
566
            return li;
567
        }
568
        /**
569
         * Render all layers that are children of a group.
570
         * @param map The map instance.
571
         * @param lyr Group layer whose children will be rendered.
572
         * @param elm DOM element that children will be appended to.
573
         * @param options Options for groups and layers
574
         * @protected
575
         */
576
577
    }, {
578
        key: 'renderLayers_',
579
        value: function renderLayers_(map, lyr, elm, options, render) {
580
            var lyrs = lyr.getLayers().getArray().slice();
581
            if (options.reverse) lyrs = lyrs.reverse();
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
582
            for (var i = 0, l; i < lyrs.length; i++) {
583
                l = lyrs[i];
584
                if (l.get('title')) {
585
                    elm.appendChild(LayerSwitcher.renderLayer_(map, l, i, options, render));
586
                }
587
            }
588
        }
589
        /**
590
         * **_[static]_** - Call the supplied function for each layer in the passed layer group
591
         * recursing nested groups.
592
         * @param lyr The layer group to start iterating from.
593
         * @param fn Callback which will be called for each layer
594
         * found under `lyr`.
595
         */
596
597
    }, {
598
        key: 'forEachRecursive',
599
        value: function forEachRecursive(lyr, fn) {
600
            lyr.getLayers().forEach(function (lyr, idx, a) {
601
                fn(lyr, idx, a);
602
                if (lyr instanceof LayerGroup) {
603
                    LayerSwitcher.forEachRecursive(lyr, fn);
604
                }
605
            });
606
        }
607
        /**
608
         * **_[static]_** - Generate a UUID
609
         * Adapted from http://stackoverflow.com/a/2117523/526860
610
         * @returns {String} UUID
611
         */
612
613
    }, {
614
        key: 'uuid',
615
        value: function uuid() {
616
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
617
                var r = Math.random() * 16 | 0,
618
                    v = c == 'x' ? r : r & 0x3 | 0x8;
619
                return v.toString(16);
620
            });
621
        }
622
        /**
623
         * Apply workaround to enable scrolling of overflowing content within an
624
         * element. Adapted from https://gist.github.com/chrismbarr/4107472
625
         * @param elm Element on which to enable touch scrolling
626
         * @protected
627
         */
628
629
    }, {
630
        key: 'enableTouchScroll_',
631
        value: function enableTouchScroll_(elm) {
632
            if (LayerSwitcher.isTouchDevice_()) {
633
                var scrollStartPos = 0;
634
                elm.addEventListener('touchstart', function (event) {
635
                    scrollStartPos = this.scrollTop + event.touches[0].pageY;
636
                }, false);
637
                elm.addEventListener('touchmove', function (event) {
638
                    this.scrollTop = scrollStartPos - event.touches[0].pageY;
639
                }, false);
640
            }
641
        }
642
        /**
643
         * Determine if the current browser supports touch events. Adapted from
644
         * https://gist.github.com/chrismbarr/4107472
645
         * @returns {Boolean} True if client can have 'TouchEvent' event
646
         * @protected
647
         */
648
649
    }, {
650
        key: 'isTouchDevice_',
651
        value: function isTouchDevice_() {
652
            try {
653
                document.createEvent('TouchEvent');
654
                return true;
655
            } catch (e) {
656
                return false;
657
            }
658
        }
659
        /**
660
         * Fold/unfold layer group
661
         * @param lyr Layer group to fold/unfold
662
         * @param li List item containing layer group
663
         * @protected
664
         */
665
666
    }, {
667
        key: 'toggleFold_',
668
        value: function toggleFold_(lyr, li) {
669
            li.classList.remove(CSS_PREFIX + lyr.get('fold'));
670
            lyr.set('fold', lyr.get('fold') === 'open' ? 'close' : 'open');
671
            li.classList.add(CSS_PREFIX + lyr.get('fold'));
672
        }
673
        /**
674
         * If a valid groupSelectStyle value is not provided then return the default
675
         * @param groupSelectStyle The string to check for validity
676
         * @returns The value groupSelectStyle, if valid, the default otherwise
677
         * @protected
678
         */
679
680
    }, {
681
        key: 'getGroupSelectStyle',
682
        value: function getGroupSelectStyle(groupSelectStyle) {
683
            return ['none', 'children', 'group'].indexOf(groupSelectStyle) >= 0 ? groupSelectStyle : 'children';
684
        }
685
    }]);
686
    return LayerSwitcher;
687
}(Control);
688
if (window['ol'] && window['ol']['control']) {
689
    window['ol']['control']['LayerSwitcher'] = LayerSwitcher;
690
}
691
692
return LayerSwitcher;
693
694
})));
695